
//r3 - added tankgallons to class trip


//mpguino, open source fuel consumption system
//GPL Software, mass production use rights reserved by opengauge.org, personal use is perfectly fine , no warranties expressed or implied

//Special thanks to the good folks at ecomodder.com, ardunio.cc, avrfreaks.net, cadsoft.de, atmel.com,
//and all the folks who donate their time and resources and share their experiences freely


//My hardware:  

/* External connections:

 Vehicle interface pins
 injector open D2 (int0)
 injector closed D3 (int1)
 speed C0 (pcint8) Not used in this project

 LCD Pins
 DI  D4
 DB4 D7
 DB5 B0
 DB6 B4
 DB7 B5
 Enable C5
 Contrast D6, controlled by PWM on OC0A
 Brightness B1, controlled by PWM on OC1A
 Goal of using the I2C controller on the LCD
 

 Buttons
 left C3 (pcint11)
 middle  C4 (pcint12)
 right C5 (pcint13)

 */

/* Program overview (easier said than done)
 set up digital pins to drive the lcd
 set up pwm pins for lcd brightness and contrast
 set up interrupts for the buttons, the speed signal, and the injector high/low signals.
 set up tx pin for transmitting values over uart

 create accumulators for speed/injector data

 mainloop{
 incorporate the accumulators into longer storage trips, reset accumulators
 display computations, transmit accumulators
 scan for key presses and perform their function (change screen,reset a trip,goto setup)
 pause for remainder of 1/2 second
 }

 */

#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
typedef uint8_t boolean;
typedef uint8_t byte;
#define RISING 3
#define FALLING 2
#define loopsPerSecond 2 // how many times will we try and loop in a second
//use with 20mhz
#define cyclesperhour 4500
#define dispadj 800
#define dispadj2 1250
#define looptime 1250000ul/loopsPerSecond //1/2 second
#define myubbr (20000000/16/9600-1)
#define injhold (parms[injectorSettleTimeIdx]*5)/4

#define outhi(port,pin) PORT##port |= ( 1 << P##port##pin )
#define outlo(port,pin) PORT##port &= ~( 1 << P##port##pin )
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

//use with 16mhz, not tested
//#define cyclesperhour 3600
//#define dispadj 1000
//#define dispadj2 1250
//#define looptime 1000000ul/loopsPerSecond //1/2 second
//#define myubbr (16000000/16/9600-1)
//#define injhold parms[injectorSettleTimeIdx]

//#define usedefaults true
void enableLButton();
void enableMButton();
void enableRButton();
void addEvent(byte eventID, unsigned int ms);
unsigned long microSeconds(void);
unsigned long elapsedMicroseconds(unsigned long startMicroSeconds,
		unsigned long currentMicroseconds);
unsigned long elapsedMicroseconds(unsigned long startMicroSeconds);
void processInjOpen(void);
void processInjClosed(void);
void enableVSS();
void setup(void);
void mainloop(void);
char* format(unsigned long num);
char * getStr(prog_char * str);

void doDisplayCustom();
void doDisplayEOCIdleData();
void doDisplayInstantCurrent();
void doDisplayInstantTank();
void doDisplayBigInstant();
void doDisplayBigCurrent();
void doDisplayBigTank();
void doDisplayCurrentTripData(void);
void doDisplayTankTripData(void);
void doDisplaySystemInfo(void);
void displayTripCombo(char *lu1, char * lm1, unsigned long v1, char * lu2, char * lm2, unsigned long v2,
		char * lu3, char * lm3, unsigned long v3, char * lu4, char * lm4, unsigned long v4);
void tDisplay(void * r);


int memoryTest();
unsigned long instantmph();
unsigned long instantmpg();
unsigned long instantgph();
void bigNum(unsigned long t, char * txt1, const char * txt2);
void init64(unsigned long an[], unsigned long bigPart,
				unsigned long littlePart);
void shl64(unsigned long an[]);
void shr64(unsigned long an[]);
void add64(unsigned long an[], unsigned long ann[]);
void sub64(unsigned long an[], unsigned long ann[]);
boolean eq64(unsigned long an[], unsigned long ann[]);
boolean lt64(unsigned long an[], unsigned long ann[]);
void div64(unsigned long num[], unsigned long den[]);
void mul64(unsigned long an[], unsigned long ann[]);


void save();
byte load();
char * uformat(unsigned long val);
unsigned long rformat(char * val);
void editParm(byte parmIdx);
void initGuino();
unsigned long millis2();
void delay2(unsigned long ms);
void delayMicroseconds2(unsigned int us);


void simpletx(char * string);
unsigned long parms[] = { 55ul, 8208ul, 500000000ul, 3ul, 420000000ul, 10300ul,
		500ul, 2400ul, 0ul, 2ul, 0ul, 0ul };//default values
char * parmLabels[] = { "Contrast", "VSS Pulses/Mile", "MicroSec/Gallon",
		"Pulses/2 revs", "Timout(microSec)", "Tank Gal * 1000",
		"Injector DelayuS", "Weight (lbs)", "Scratchpad(odo?)", "VSS Delay ms",
		"InjTrg 0-Dn 1-Up", "Metric (1=yes)" };



//byte brightness[]={255,214,171,128}; //middle button cycles through these brightness settings
byte brightness[] = { 0, 41, 84, 128 }; //middle button cycles through these brightness settings
#define brightnessLength (sizeof(brightness)/sizeof(byte)) //array size
byte brightnessIdx = 1;



#define contrastIdx 0  //do contrast first to get display dialed in
#define vssPulsesPerMileIdx 1
volatile unsigned long distancefactor;

#define microSecondsPerGallonIdx 2
volatile unsigned long fuelfactor;

#define injPulsesPer2Revolutions 3
#define currentTripResetTimeoutUSIdx 4
#define tankSizeIdx 5
#define injectorSettleTimeIdx 6
#define weightIdx 7
#define scratchpadIdx 8
#define vsspauseIdx 9
#define injEdgeIdx 10
#define metricIdx 11
#define parmsLength (sizeof(parms)/sizeof(unsigned long)) /*  //array size      */

unsigned long injectorSettleTime;

#define nil 3999999999ul

#define guinosigold 0b10100101
#define guinosig 0b11100111

#define vssBit ( 1 << 0 )
#define lbuttonBit ( 1 << 3 )
#define mbuttonBit ( 1 << 4 )
#define rbuttonBit ( 1 << 5 )

typedef void (* pFunc)(void);//type for display function pointers

volatile unsigned long timer2_overflow_count;

/*** Set up the Events ***
 We have our own ISR for timer2 which gets called about once a millisecond.
 So we define certain event functions that we can schedule by calling addEvent
 with the event ID and the number of milliseconds to wait before calling the event.
 The milliseconds is approximate.

 Keep the event functions SMALL!!!  This is an interrupt!

 */
//event functions

void enableLButton() {
	PCMSK1 |= (1 << PCINT11);
}
void enableMButton() {
	PCMSK1 |= (1 << PCINT12);
}
void enableRButton() {
	PCMSK1 |= (1 << PCINT13);
}
//array of the event functions
pFunc eventFuncs[] = { enableVSS, enableLButton, enableMButton, enableRButton };
#define eventFuncSize (sizeof(eventFuncs)/sizeof(pFunc))
//define the event IDs
#define enableVSSID 0
#define enableLButtonID 1
#define enableMButtonID 2
#define enableRButtonID 3
//ms counters
unsigned int eventFuncCounts[eventFuncSize];

//schedule an event to occur ms milliseconds from now
void addEvent(byte eventID, unsigned int ms) {
	if (ms == 0)
		eventFuncs[eventID]();
	else
		eventFuncCounts[eventID] = ms;
}

/* this ISR gets called every 1.024 milliseconds, we will call that a millisecond for our purposes
 go through all the event counts,
 if any are non zero subtract 1 and call the associated function if it just turned zero.  */
ISR(TIMER2_OVF_vect)
{
	timer2_overflow_count++;
	for (byte eventID = 0; eventID < eventFuncSize; eventID++) {
		if (eventFuncCounts[eventID] != 0) {
			eventFuncCounts[eventID]--;
			if (eventFuncCounts[eventID] == 0)
				eventFuncs[eventID]();
		}
	}
}

unsigned long maxLoopLength = 0; //see if we are overutilizing the CPU


#define buttonsUp   lbuttonBit + mbuttonBit + rbuttonBit  // start with the buttons in the right state
byte buttonState = buttonsUp;

//overflow counter used by millis2()
unsigned long lastMicroSeconds = millis2() * 1000;
unsigned long microSeconds(void) {
	unsigned long tmp_timer2_overflow_count;
	unsigned long tmp;
	byte tmp_tcnt2;
	cli();
	//disable interrupts
	tmp_timer2_overflow_count = timer2_overflow_count;
	tmp_tcnt2 = TCNT2;
	sei();
	// enable interrupts
	tmp = ((tmp_timer2_overflow_count << 8) + tmp_tcnt2) * 4;
	if ((tmp <= lastMicroSeconds) && (lastMicroSeconds < 4290560000ul))
		return microSeconds();
	lastMicroSeconds = tmp;
	return tmp;
}

unsigned long elapsedMicroseconds(unsigned long startMicroSeconds,
		unsigned long currentMicroseconds) {
	if (currentMicroseconds >= startMicroSeconds)
		return currentMicroseconds - startMicroSeconds;
	return 4294967295 - (startMicroSeconds - currentMicroseconds);
}

unsigned long elapsedMicroseconds(unsigned long startMicroSeconds) {
	return elapsedMicroseconds(startMicroSeconds, microSeconds());
}

//Trip prototype
class Trip {
public:
	unsigned long loopCount; //how long has this trip been running
	unsigned long injPulses; //rpm
	unsigned long injHiSec;// seconds the injector has been open
	unsigned long injHius;// microseconds, fractional part of the injectors open
	unsigned long injIdleHiSec;// seconds the injector has been open
	unsigned long injIdleHius;// microseconds, fractional part of the injectors open
	unsigned long vssPulses;//from the speedo
	unsigned long vssEOCPulses;//from the speedo
	unsigned long vssPulseLength; // only used by instant
	//these functions actually return in thousandths,
	unsigned long miles();
  unsigned long tankgallons(); //added to try and capture the tank size
  unsigned long gallons();
	unsigned long lkm();
  unsigned long gph();
	unsigned long mpg();
	unsigned long mph();
	unsigned long time(); //mmm.ss
	unsigned long eocMiles(); //how many "free" miles?
	unsigned long idleGallons(); //how many gallons spent at 0 mph?
	void update(Trip t);
	void reset();
	Trip();
};

//LCD prototype
namespace LCD {
void gotoXY(byte x, byte y);
void print(char * string);
void init();
void tickleEnable();
void cmdWriteSet();
void LcdCommandWrite(byte value);
void LcdDataWrite(byte value);
byte pushNibble(byte value);
}
;

//main objects we will be working with:
unsigned long injHiStart; //for timing injector pulses
Trip tmpTrip;
Trip instant;
Trip current;
Trip tank;

unsigned volatile long instInjStart = nil;
unsigned volatile long tmpInstInjStart = nil;
unsigned volatile long instInjEnd;
unsigned volatile long tmpInstInjEnd;
unsigned volatile long instInjTot;
unsigned volatile long tmpInstInjTot;
unsigned volatile long instInjCount;
unsigned volatile long tmpInstInjCount;

volatile static pFunc int0Func;
ISR(INT0_vect)
{ //processInjOpen by default
	int0Func();
}

volatile static pFunc int1Func;
ISR(INT1_vect)
{//processInjClosed
	int1Func();
}

void processInjOpen(void) {
	injHiStart = microSeconds();
}

void processInjClosed(void) {
	long t = microSeconds();
	long x = elapsedMicroseconds(injHiStart, t) - injectorSettleTime;
	if (x > 0)
		tmpTrip.injHius += x;
	tmpTrip.injPulses++;

	if (tmpInstInjStart != nil) {
		if (x > 0)
			tmpInstInjTot += x;
		tmpInstInjCount++;
	} else {
		tmpInstInjStart = t;
	}

	tmpInstInjEnd = t;
}

volatile boolean vssFlop = 0;

void enableVSS() {
	//    tmpTrip.vssPulses++;
	vssFlop = !vssFlop;
}

unsigned volatile long lastVSS1;
unsigned volatile long lastVSSTime;
unsigned volatile long lastVSS2;

volatile boolean lastVssFlop = vssFlop;

//attach the vss/buttons interrupt
ISR( PCINT1_vect )
{
	static byte vsspinstate = 0;
	byte p = PINC;//bypassing digitalRead for interrupt performance
	if ((p & vssBit) != (vsspinstate & vssBit)) {
		addEvent(enableVSSID, parms[vsspauseIdx]); //check back in a couple milli
	}
	if (lastVssFlop != vssFlop) {
		lastVSS1 = lastVSS2;
		unsigned long t = microSeconds();
		lastVSS2 = elapsedMicroseconds(lastVSSTime, t);
		lastVSSTime = t;
		tmpTrip.vssPulses++;
		tmpTrip.vssPulseLength += lastVSS2;
		lastVssFlop = vssFlop;
	}
	vsspinstate = p;
	buttonState &= p;
}

//slim down displayed screens
/* pFunc displayFuncs[] = { doDisplayCustom, doDisplayInstantCurrent,
		doDisplayInstantTank, doDisplayBigInstant, doDisplayBigCurrent,
		doDisplayBigTank, doDisplayCurrentTripData, doDisplayTankTripData,
		doDisplayEOCIdleData, doDisplaySystemInfo, };
    */  

pFunc displayFuncs[] = { doDisplayCustom, doDisplayBigInstant, doDisplaySystemInfo, };


#define displayFuncSize (sizeof(displayFuncs)/sizeof(pFunc)) //array size
prog_char * displayFuncNames[displayFuncSize];
byte newRun = 0;
void setup(void) {

	newRun = load();//load the default parameters
	byte x = 0;
	displayFuncNames[x++] = PSTR("Custom  ");
	displayFuncNames[x++] = PSTR("Instant/Current ");
	displayFuncNames[x++] = PSTR("Instant/Tank ");
	displayFuncNames[x++] = PSTR("BIG Instant ");
	displayFuncNames[x++] = PSTR("BIG Current ");
	displayFuncNames[x++] = PSTR("BIG Tank ");
	displayFuncNames[x++] = PSTR("Current ");
	displayFuncNames[x++] = PSTR("Tank ");
	displayFuncNames[x++] = PSTR("EOC/Idle ");
	displayFuncNames[x++] = PSTR("CPU Monitor ");

	//  analogWrite(BrightnessPin,brightness[brightnessIdx]);
	sbi(TCCR1A, COM1A1); //brightness pwm enable

	OCR1A = brightness[brightnessIdx];

	DDRB = (1 << DDB5) | (1 << DDB4) | (1 << DDB1) | (1 << DDB0);
	DDRD = (1 << DDD7) | (1 << DDD6) | (1 << DDD5) | (1 << DDD4);

	delay2(500);

	//  analogWrite(ContrastPin,parms[contrastIdx]);
	sbi(TCCR0A, COM0A1);//contrast pwm enable
	OCR0A = parms[contrastIdx];

	LCD::init();
	LCD::LcdCommandWrite(0b00000001); // clear display, set cursor position to zero
	LCD::LcdCommandWrite(0b10000); // set dram to zero
	LCD::gotoXY(0, 0);
	LCD::print(getStr(PSTR("Marineduino 0.10")));
	LCD::gotoXY(0, 1);
	LCD::print(getStr(PSTR("  MPGuino  v0.86")));

	injectorSettleTime = injhold;
	int0Func = processInjOpen;
	int1Func = processInjClosed;

	//set up the external interrupts
	EICRA = (EICRA & ~((1 << ISC00) | (1 << ISC01)))
			| ((parms[injEdgeIdx] == 1 ? RISING : FALLING) << ISC00);
	EIMSK |= (1 << INT0);
	EICRA = (EICRA & ~((1 << ISC10) | (1 << ISC11)))
			| ((parms[injEdgeIdx] == 1 ? FALLING : RISING) << ISC10);
	EIMSK |= (1 << INT1);

	PORTC |= (1 << 5) | (1 << 4) | (1 << 3); //button pullup resistors

	distancefactor = parms[vssPulsesPerMileIdx];
	fuelfactor = parms[microSecondsPerGallonIdx];

	if (parms[metricIdx] == 1){
		distancefactor /= 1.609;
		fuelfactor /= 3.785;
	}

	//low level interrupt enable stuff
	PCMSK1 |= (1 << PCINT8);
	enableLButton();
	enableMButton();
	enableRButton();
	PCICR |= (1 << PCIE1);

	delay2(1500);
}

byte screen = 0;
byte holdDisplay = 0;

void mainloop(void) {
	if (newRun != 1)
		initGuino();//go through the initialization screen
	unsigned long lastActivity = microSeconds();
	unsigned long tankHold; //state at point of last activity
	while (true) {
		unsigned long loopStart = microSeconds();
		instant.reset(); //clear instant
		cli();
		instant.update(tmpTrip); //"copy" of tmpTrip in instant now
		tmpTrip.reset(); //reset tmpTrip first so we don't lose too many interrupts
		instInjStart = tmpInstInjStart;
		instInjEnd = tmpInstInjEnd;
		instInjTot = tmpInstInjTot;
		instInjCount = tmpInstInjCount;

		tmpInstInjStart = nil;
		tmpInstInjEnd = nil;
		tmpInstInjTot = 0;
		tmpInstInjCount = 0;

		sei();

		//send out instantmpg * 1000, instantmph * 1000, the injector/vss raw data
		simpletx(format(instantmpg()));
		simpletx(",");
		simpletx(format(instantmph()));
		simpletx(",");
		simpletx(format(instant.injHius * 1000));
		simpletx(",");
		simpletx(format(instant.injPulses * 1000));
		simpletx(",");
		simpletx(format(instant.vssPulses * 1000));
		simpletx("\n");

		current.update(instant); //use instant to update current
		tank.update(instant); //use instant to update tank

		//currentTripResetTimeoutUS
		if (instant.vssPulses == 0 && instant.injPulses == 0 && holdDisplay
				== 0) {
			if (elapsedMicroseconds(lastActivity)
					> parms[currentTripResetTimeoutUSIdx] && lastActivity
					!= nil) {
				//        analogWrite(BrightnessPin,brightness[0]);    //nitey night
				OCR1A = brightness[0];

				lastActivity = nil;
			}
		} else {
			if (lastActivity == nil) {//wake up!!!
				OCR1A = brightness[brightnessIdx];
				//        analogWrite(BrightnessPin,brightness[brightnessIdx]);
				lastActivity = loopStart;
				current.reset();
				tank.loopCount = tankHold;
				current.update(instant);
				tank.update(instant);
			} else {
				lastActivity = loopStart;
				tankHold = tank.loopCount;
			}
		}

		if (holdDisplay == 0) {
			displayFuncs[screen](); //call the appropriate display routine
			LCD::gotoXY(0, 0);

			//see if any buttons were pressed, display a brief message if so
			if (!(buttonState & lbuttonBit) && !(buttonState & rbuttonBit)) {// left and right = initialize
				LCD::print(getStr(PSTR("Setup ")));
				initGuino();
				//}else if(!(buttonState&lbuttonBit) && !(buttonState&rbuttonBit)){// left and right = run lcd init = tank reset
				//    LCD::print(getStr(PSTR("Init LCD ")));
				//    LCD::init();
			} else if (!(buttonState & lbuttonBit) && !(buttonState
					&mbuttonBit)) {// left and middle = tank reset
				tank.reset();
				LCD::print(getStr(PSTR("Tank Reset ")));
			} else if (!(buttonState & mbuttonBit) && !(buttonState
					&rbuttonBit)) {// right and middle = current reset
				current.reset();
				LCD::print(getStr(PSTR("Current Reset ")));
			} else if (!(buttonState & lbuttonBit)) { //left is rotate through screeens to the left
				if (screen != 0)
					screen = (screen - 1);
				else
					screen = displayFuncSize - 1;
				LCD::print(getStr(displayFuncNames[screen]));
			} else if (!(buttonState & mbuttonBit)) { //middle is cycle through brightness settings
				brightnessIdx = (brightnessIdx + 1) % brightnessLength;
				OCR1A = brightness[brightnessIdx];
				//        analogWrite(BrightnessPin,brightness[brightnessIdx]);
				LCD::print(getStr(PSTR("Brightness ")));
				LCD::LcdDataWrite('0' + brightnessIdx);
				LCD::print(" ");
			} else if (!(buttonState & rbuttonBit)) {//right is rotate through screeens to the left
				screen = (screen + 1) % displayFuncSize;
				LCD::print(getStr(displayFuncNames[screen]));
			}
			if (buttonState != buttonsUp)
				holdDisplay = 1;
		} else {
			holdDisplay = 0;
		}
		buttonState = buttonsUp;//reset the buttons

		//keep track of how long the loops take before we go int waiting.
		unsigned long loopX = elapsedMicroseconds(loopStart);
		if (loopX > maxLoopLength)
			maxLoopLength = loopX;

		while (elapsedMicroseconds(loopStart) < (looptime))
			;//wait for the end of a second to arrive
	}

}

char fBuff[7];//used by format

void dispv(char * usl, char * ml, unsigned long num) {
	LCD::print(parms[metricIdx]==1?ml:usl);
	LCD::print(format(num));
}


char* format(unsigned long num) {
	byte dp = 3;

	while (num > 999999) {
		num /= 10;
		dp++;
		if (dp == 5)
			break; // We'll lose the top numbers like an odometer
	}
	if (dp == 5)
		dp = 99; // We don't need a decimal point here.

	// Round off the non-printed value.
	if ((num % 10) > 4)
		num += 10;
	num /= 10;
	byte x = 6;
	while (x > 0) {
		x--;
		if (x == dp) { //time to poke in the decimal point?{
			fBuff[x] = '.';
		} else {
			fBuff[x] = '0' + (num % 10);//poke the ascii character for the digit.
			num /= 10;
		}
	}
	fBuff[6] = 0;
	return fBuff;
}
//get a string from flash
char mBuff[17];//used by getStr
char * getStr(prog_char * str) {
	strcpy_P(mBuff, str);
	return mBuff;
}

void doDisplayCustom(){displayTripCombo('S',instantgph(),'C',current.injIdleHiSec*1000),
       'G','L',tank.gallons,'S',tank.gph;}

//void doDisplayCustom() {displayTripCombo("MG","LK", instantmpg(), " S", " S", instantmph(), "GH","LH",instantgph(), " C"," C", current.mpg());}
//void doDisplayCustom(){displayTripCombo('I','M',instantmpg(),'S',instantgph(),'R','P',instantrpm(),'C',current.injIdleHiSec*1000);}
//void doDisplayCustom(){displayTripCombo('I','M',995,'S',994,'R','P',999994,'C',999995);}
void doDisplayEOCIdleData() {
	displayTripCombo("CE","CE", current.eocMiles(), " G"," L", current.idleGallons(),
			"TE","TE", tank.eocMiles(), " G"," L", tank.idleGallons());
}
void doDisplayInstantCurrent() {
	displayTripCombo("IM","IL", instantmpg(), " S"," S", instantmph(), "CM","CL",
			current.mpg(), " D"," D", current.miles());
}

void doDisplayInstantTank() {
	displayTripCombo("IM","IL", instantmpg(), " S"," S", instantmph(), "TM","TL",
			tank.mpg(), " D"," D", tank.miles());
}


void doDisplayBigInstant() {
	bigNum(instantgph(),"INST","GPH ");
}


//void doDisplayBigInstant() {
//	bigNum(instantmpg(), "INST", parms[metricIdx]==1?"L/K ":"MPG ");
//}

void doDisplayBigCurrent() {
	bigNum(current.mpg(), "CURR", parms[metricIdx]==1?"L/K ":"MPG ");
}
void doDisplayBigTank() {
	bigNum(tank.mpg(), "TANK", parms[metricIdx]==1?"L/K ":"MPG ");
}


void doDisplayCurrentTripData(void) {
	tDisplay(&current);
} //display current trip formatted data.
void doDisplayTankTripData(void) {
	tDisplay(&tank);
} //display tank trip formatted data.
void doDisplaySystemInfo(void) {
	LCD::gotoXY(0, 0);
	LCD::print("C%");
	LCD::print(format(maxLoopLength * 1000 / (looptime / 100)));
	LCD::print(" T");
	LCD::print(format(tank.time()));
	unsigned long mem = memoryTest();
	mem *= 1000;
	LCD::gotoXY(0, 1);
	LCD::print("FREE MEM:");
	LCD::print(format(mem));
//	LCD::print(" D");
//	LCD::print(format(readTemp()));
} //display max cpu utilization and ram.

void displayTripCombo(char *lu1, char * lm1, unsigned long v1, char * lu2, char * lm2, unsigned long v2,
		char * lu3, char * lm3, unsigned long v3, char * lu4, char * lm4, unsigned long v4) {
	LCD::gotoXY(0, 0);
	dispv(lu1, lm1, v1);
	dispv(lu2, lm2, v2);
	LCD::gotoXY(0, 1);
	dispv(lu3, lm3, v3);
	dispv(lu4, lm4, v4);
}

//arduino doesn't do well with types defined in a script as parameters, so have to pass as void * and use -> notation.
void tDisplay(void * r) { //display trip functions.
	Trip *t = (Trip *) r;
	LCD::gotoXY(0, 0);
	dispv("MH","KH",t->mph());
	dispv("MG","LK",t->mpg());
	LCD::gotoXY(0, 1);
	dispv("MI","KM",t->miles());
	dispv("GA"," L",t->gallons());
}



//x=0..16, y= 0..1
void LCD::gotoXY(byte x, byte y) {
	byte dr = x + 0x80;
	if (y == 1)
		dr += 0x40;
	if (y == 2)
		dr += 0x14;
	if (y == 3)
		dr += 0x54;
	LCD::LcdCommandWrite(dr);
}

void LCD::print(char * string) {
	byte x = 0;
	char c = string[x];
	while (c != 0) {
		LCD::LcdDataWrite(c);
		x++;
		c = string[x];
	}
}

void LCD::init() {
	delay2(16); // wait for more than 15 msec
	pushNibble(0b00110000); // send (B0011) to DB7-4
	cmdWriteSet();
	tickleEnable();
	delay2(5); // wait for more than 4.1 msec
	pushNibble(0b00110000); // send (B0011) to DB7-4
	cmdWriteSet();
	tickleEnable();
	delay2(1); // wait for more than 100 usec
	pushNibble(0b00110000); // send (B0011) to DB7-4
	cmdWriteSet();
	tickleEnable();
	delay2(1); // wait for more than 100 usec
	pushNibble(0b00100000); // send (B0010) to DB7-4 for 4bit
	cmdWriteSet();
	tickleEnable();
	delay2(1); // wait for more than 100 usec
	// ready to use normal LcdCommandWrite() function now!
	LcdCommandWrite(0b00101000); // 4-bit interface, 2 display lines, 5x8 font
	LcdCommandWrite(0b00001100); // display control:
	LcdCommandWrite(0b00000110); // entry mode set: increment automatically, no display shift

	//creating the custom fonts:
	LcdCommandWrite(0b01001000); // set cgram
	static byte chars[] PROGMEM = { 0b11111, 0b00000, 0b11111, 0b11111,
			0b00000, 0b11111, 0b00000, 0b11111, 0b11111, 0b00000, 0b00000,
			0b00000, 0b00000, 0b11111, 0b00000, 0b00000, 0b00000, 0b00000,
			0b11111, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b00000,
			0b00000, 0b00000, 0b00000, 0b11111, 0b01110, 0b00000, 0b11111,
			0b11111, 0b11111, 0b01110, 0b00000, 0b11111, 0b11111, 0b11111,
			0b01110 };

	for (byte x = 0; x < 5; x++)
		for (byte y = 0; y < 8; y++)
			LcdDataWrite(pgm_read_byte(&chars[y*5+x])); //write the character data to the character generator ram
	LcdCommandWrite(0b00000001); // clear display, set cursor position to zero
	LcdCommandWrite(0b10000000); // set dram to zero

}

void LCD::tickleEnable() {
	// send a pulse to enable  PD5
	PORTD |= (1 << 5);
	delayMicroseconds2(1); // pause 1 ms according to datasheet
	PORTD &= ~(1 << 5);
	delayMicroseconds2(1); // pause 1 ms according to datasheet
}

void LCD::cmdWriteSet() { //set enable (PD5) low and DI(PD4) low
	PORTD &= ~(1 << 5);
	delayMicroseconds2(1); // pause 1 ms according to datasheet
	PORTD &= ~(1 << 4);
}

byte LCD::pushNibble(byte value) { //db7=PB5, db6=PB4, db5 = PB0, db4  = PD7
	value & 128 ? PORTB |= (1 << 5) : PORTB &= ~(1 << 5);
	value <<= 1;
	value & 128 ? PORTB |= (1 << 4) : PORTB &= ~(1 << 4);
	value <<= 1;
	value & 128 ? PORTB |= (1 << 0) : PORTB &= ~(1 << 0);
	value <<= 1;
	value & 128 ? PORTD |= (1 << 7) : PORTD &= ~(1 << 7);
	value <<= 1;
	return value;
}

void LCD::LcdCommandWrite(byte value) {
	value = pushNibble(value);
	cmdWriteSet();
	tickleEnable();
	value = pushNibble(value);
	cmdWriteSet();
	tickleEnable();
	delay2(5);
}

void LCD::LcdDataWrite(byte value) {
	PORTD |= (1 << 4); //di on pd4
	value = pushNibble(value);
	tickleEnable();
	value = pushNibble(value);
	tickleEnable();
	delay2(5);
}

// this function will return the number of bytes currently free in RAM
extern int __bss_end;
extern int *__brkval;
int memoryTest() {
	int free_memory;
	if ((int) __brkval == 0)
		free_memory = ((int) &free_memory) - ((int) &__bss_end);
	else
		free_memory = ((int) &free_memory) - ((int) __brkval);
	return free_memory;
}

Trip::Trip() {
}

//for display computing
unsigned long tmp1[2];
unsigned long tmp2[2];
unsigned long tmp3[2];

unsigned long instantmph() {
	//unsigned long vssPulseTimeuS = (lastVSS1 + lastVSS2) / 2;
//	unsigned long vssPulseTimeuS = instant.vssPulseLength / instant.vssPulses;

 //  init64(tmp1, 0, 1000000000ul);
 //  init64(tmp2, 0, distancefactor);
 //  div64(tmp1, tmp2);
 //  init64(tmp2, 0, cyclesperhour);
 //  mul64(tmp1, tmp2);
 //  init64(tmp2, 0, vssPulseTimeuS);
 //  div64(tmp1, tmp2);
// this is where I need to use tinyGPS 
 
	return 20;
}

unsigned long instantlkm() {
	unsigned long imph = instantmph();
	unsigned long igph = instantgph();
	if (igph == 0)
		return 0;
	if (imph == 0)
		return 999999000;
	init64(tmp1, 0, 100000ul);
	init64(tmp2, 0, igph);
	mul64(tmp2, tmp1);
	init64(tmp1, 0, imph);
        div64(tmp2,tmp1);
	return tmp2[1];
}

unsigned long instantmpg() {
	if(parms[metricIdx]==1)
		return instantlkm();
	unsigned long imph = instantmph();
	unsigned long igph = instantgph();
	if (imph == 0)
		return 0;
	if (igph == 0)
		return 999999000;
	init64(tmp1, 0, 1000ul);
	init64(tmp2, 0, imph);
	mul64(tmp1, tmp2);
	init64(tmp2, 0, igph);
    div64(tmp1,tmp2);
	return tmp1[1];

}

unsigned long instantgph() {
	//  unsigned long vssPulseTimeuS = instant.vssPulseLength/instant.vssPulses;

	//  unsigned long instInjStart=nil;
	//unsigned long instInjEnd;
	//unsigned long instInjTot;
	init64(tmp1, 0, instInjTot);
	init64(tmp2, 0, 3600000000ul);
	mul64(tmp1, tmp2);
	init64(tmp2, 0, 1000ul);
	mul64(tmp1, tmp2);
	init64(tmp2, 0, fuelfactor);
	div64(tmp1, tmp2);
	init64(tmp2, 0, instInjEnd - instInjStart);
	div64(tmp1, tmp2);
	return tmp1[1];
}
/*
 unsigned long instantrpm(){
 init64(tmp1,0,instInjCount);
 init64(tmp2,0,120000000ul);
 mul64(tmp1,tmp2);
 init64(tmp2,0,1000ul);
 mul64(tmp1,tmp2);
 init64(tmp2,0,parms[injPulsesPer2Revolutions]);
 div64(tmp1,tmp2);
 init64(tmp2,0,instInjEnd-instInjStart);
 div64(tmp1,tmp2);
 return tmp1[1];
 } */

unsigned long Trip::miles() {
	init64(tmp1, 0, vssPulses);
	init64(tmp2, 0, 1000);
	mul64(tmp1, tmp2);
	init64(tmp2, 0, distancefactor);
	div64(tmp1, tmp2);
	return tmp1[1];
}

unsigned long Trip::eocMiles() {
	init64(tmp1, 0, vssEOCPulses);
	init64(tmp2, 0, 1000);
	mul64(tmp1, tmp2);
	init64(tmp2, 0, distancefactor);
	div64(tmp1, tmp2);
	return tmp1[1];
}

unsigned long Trip::mph() {
	if (loopCount == 0)
		return 0;
 //  init64(tmp1, 0, loopsPerSecond);
 //  init64(tmp2, 0, vssPulses);
 //  mul64(tmp1, tmp2);
 //  init64(tmp2, 0, 3600000ul);
 //  mul64(tmp1, tmp2);
 //  init64(tmp2, 0, distancefactor);
 //  div64(tmp1, tmp2);
 //  init64(tmp2, 0, loopCount);
 //  div64(tmp1, tmp2);
 //  return tmp1[1];
    return 20;
}

unsigned long Trip::gallons() {
	init64(tmp1, 0, injHiSec);
	init64(tmp2, 0, 1000000);
	mul64(tmp1, tmp2);
	init64(tmp2, 0, injHius);
	add64(tmp1, tmp2);
	//  init64(tmp2,0,1250);
	init64(tmp2, 0, dispadj);
	mul64(tmp1, tmp2);
	init64(tmp2, 0, fuelfactor);
	div64(tmp1, tmp2);
	return tmp1[1];
}

unsigned long Trip::idleGallons() {
	init64(tmp1, 0, injIdleHiSec);
	init64(tmp2, 0, 1000000);
	mul64(tmp1, tmp2);
	init64(tmp2, 0, injIdleHius);
	add64(tmp1, tmp2);
	init64(tmp2, 0, dispadj);
	mul64(tmp1, tmp2);
	init64(tmp2, 0, fuelfactor);
	div64(tmp1, tmp2);
	return tmp1[1];
}

//eocMiles
//idleGallons

unsigned long Trip::lkm() {
	if (injPulses == 0)
		return 0;
	if (vssPulses == 0)
		return 999999000; //who doesn't like to see 999999?  :)


	init64(tmp1, 0, injHiSec);
	init64(tmp3, 0, 1000000ul);
	mul64(tmp3, tmp1);
	init64(tmp1, 0, injHius);
	add64(tmp3, tmp1);
	init64(tmp1, 0, distancefactor);
	mul64(tmp3, tmp1);
	init64(tmp1, 0, 80000ul);
//	init64(tmp1, 0, 100000000);
	mul64(tmp3, tmp1);

	init64(tmp1, 0, fuelfactor);
//	init64(tmp2, 0, dispadj2);
//	mul64(tmp1, tmp2);
	init64(tmp2, 0, vssPulses);
	mul64(tmp1, tmp2);

	div64(tmp3, tmp1);
	return tmp3[1];
}

unsigned long Trip::mpg() {
	if(parms[metricIdx]==1)
		return lkm();
	if (vssPulses == 0)
		return 0;
	if (injPulses == 0)
		return 999999000; //who doesn't like to see 999999?  :)

	init64(tmp1, 0, injHiSec);
	init64(tmp3, 0, 1000000);
	mul64(tmp3, tmp1);
	init64(tmp1, 0, injHius);
	add64(tmp3, tmp1);
	init64(tmp1, 0, distancefactor);
	mul64(tmp3, tmp1);

	init64(tmp1, 0, fuelfactor);
	init64(tmp2, 0, dispadj2);
	mul64(tmp1, tmp2);
	init64(tmp2, 0, vssPulses);
	mul64(tmp1, tmp2);

	div64(tmp1, tmp3);
	return tmp1[1];
}



//return the seconds as a time mmm.ss, eventually hhh:mm too
unsigned long Trip::time() {
	//  return seconds*1000;
	byte d = 60;
	unsigned long seconds = loopCount / loopsPerSecond;
	//  if(seconds/60 > 999) d = 3600; //scale up to hours.minutes if we get past 999 minutes
	return ((seconds / d) * 1000) + ((seconds % d) * 10);
}

void Trip::reset() {
	loopCount = 0;
	injPulses = 0;
	injHius = 0;
	injHiSec = 0;
	vssPulses = 0;
	vssPulseLength = 0;
	injIdleHiSec = 0;
	injIdleHius = 0;
	vssEOCPulses = 0;
}

void Trip::update(Trip t) {
	loopCount++; //we call update once per loop
	vssPulses += t.vssPulses;
	vssPulseLength += t.vssPulseLength;
	if (t.injPulses == 0) //track distance traveled with engine off
		vssEOCPulses += t.vssPulses;

	if (t.injPulses > 2 && t.injHius < 500000) {//chasing ghosts
		injPulses += t.injPulses;
		injHius += t.injHius;
		if (injHius >= 1000000) { //rollover into the injHiSec counter
			injHiSec++;
			injHius -= 1000000;
		}
		if (t.vssPulses == 0) { //track gallons spent sitting still

			injIdleHius += t.injHius;
			if (injIdleHius >= 1000000) { //r
				injIdleHiSec++;
				injIdleHius -= 1000000;
			}
		}
	}
}

char bignumchars1[] = { 4, 1, 4, 0, 1, 4, 32, 0, 3, 3, 4, 0, 1, 3, 4, 0, 4, 2,
		4, 0, 4, 3, 3, 0, 4, 3, 3, 0, 1, 1, 4, 0, 4, 3, 4, 0, 4, 3, 4, 0 };
char bignumchars2[] = { 4, 2, 4, 0, 2, 4, 2, 0, 4, 2, 2, 0, 2, 2, 4, 0, 32, 32,
		4, 0, 2, 2, 4, 0, 4, 2, 4, 0, 32, 4, 32, 0, 4, 2, 4, 0, 2, 2, 4, 0 };

void bigNum(unsigned long t, char * txt1, const char * txt2) {
	//  unsigned long t = 98550ul;//number in thousandths
	//  unsigned long t = 9855ul;//number in thousandths
	//  char * txt1="INST";
	//  char * txt2="MPG ";
	char dp1 = 32;
	char dp2 = 32;

//	return format2(num,4,9999);


	char * r = "009.99"; //default to 999
	if (t <= 9950) {
		r = format(t ); //009.86
		dp1 = 5;
	} else if (t <= 99500) {
		r = format(t / 10); //009.86
		dp2 = 5;
	} else if (t <= 999500) {
		r = format(t / 100); //009.86
	}

	LCD::gotoXY(0, 0);
	LCD::print(bignumchars1 + (r[2] - '0') * 4);
	LCD::print(" ");
	LCD::print(bignumchars1 + (r[4] - '0') * 4);
	LCD::print(" ");
	LCD::print(bignumchars1 + (r[5] - '0') * 4);
	LCD::print(" ");
	LCD::print(txt1);

	LCD::gotoXY(0, 1);
	LCD::print(bignumchars2 + (r[2] - '0') * 4);
	LCD::LcdDataWrite(dp1);
	LCD::print(bignumchars2 + (r[4] - '0') * 4);
	LCD::LcdDataWrite(dp2);
	LCD::print(bignumchars2 + (r[5] - '0') * 4);
	LCD::print(" ");
	LCD::print((char *)txt2);
}

//the standard 64 bit math brings in  5000+ bytes
//these bring in 1214 bytes, and everything is pass by reference
unsigned long zero64[] = { 0, 0 };

void init64(unsigned long an[], unsigned long bigPart, unsigned long littlePart) {
	an[0] = bigPart;
	an[1] = littlePart;
}

//left shift 64 bit "number"
void shl64(unsigned long an[]) {
	an[0] <<= 1;
	if (an[1] & 0x80000000)
		an[0]++;
	an[1] <<= 1;
}

//right shift 64 bit "number"
void shr64(unsigned long an[]) {
	an[1] >>= 1;
	if (an[0] & 0x1)
		an[1] += 0x80000000;
	an[0] >>= 1;
}

//add ann to an
void add64(unsigned long an[], unsigned long ann[]) {
	an[0] += ann[0];
	if (an[1] + ann[1] < ann[1])
		an[0]++;
	an[1] += ann[1];
}

//subtract ann from an
void sub64(unsigned long an[], unsigned long ann[]) {
	an[0] -= ann[0];
	if (an[1] < ann[1]) {
		an[0]--;
	}
	an[1] -= ann[1];
}

//true if an == ann
boolean eq64(unsigned long an[], unsigned long ann[]) {
	return (an[0] == ann[0]) && (an[1] == ann[1]);
}

//true if an < ann
boolean lt64(unsigned long an[], unsigned long ann[]) {
	if (an[0] > ann[0])
		return false;
	return (an[0] < ann[0]) || (an[1] < ann[1]);
}

//divide num by den
void div64(unsigned long num[], unsigned long den[]) {
	unsigned long quot[2];
	unsigned long qbit[2];
	unsigned long tmp[2];
	init64(quot, 0, 0);
	init64(qbit, 0, 1);

	if (eq64(num, zero64)) { //numerator 0, call it 0
		init64(num, 0, 0);
		return;
	}

	if (eq64(den, zero64)) { //numerator not zero, denominator 0, infinity in my book.
		init64(num, 0xffffffff, 0xffffffff);
		return;
	}

	init64(tmp, 0x80000000, 0);
	while (lt64(den, tmp)) {
		shl64(den);
		shl64(qbit);
	}

	while (!eq64(qbit, zero64)) {
		if (lt64(den, num) || eq64(den, num)) {
			sub64(num, den);
			add64(quot, qbit);
		}
		shr64(den);
		shr64(qbit);
	}

	//remainder now in num, but using it to return quotient for now
	init64(num, quot[0], quot[1]);
}

//multiply num by den
void mul64(unsigned long an[], unsigned long ann[]) {
	unsigned long p[2] = { 0, 0 };
	unsigned long y[2] = { ann[0], ann[1] };
	while (!eq64(y, zero64)) {
		if (y[1] & 1)
			add64(p, an);
		shl64(an);
		shr64(y);
	}
	init64(an, p[0], p[1]);
}

void save() {
	eeprom_write_byte((unsigned char *) 0, guinosig);
	eeprom_write_byte((unsigned char *) 1, parmsLength);
	byte p = 0;
	for (int x = 4; p < parmsLength; x += 4) {
		unsigned long v = parms[p];
		eeprom_write_byte((unsigned char *) x, (v >> 24) & 255);
		eeprom_write_byte((unsigned char *) x + 1, (v >> 16) & 255);
		eeprom_write_byte((unsigned char *) x + 2, (v >> 8) & 255);
		eeprom_write_byte((unsigned char *) x + 3, (v) & 255);
		p++;
	}
}

byte load() { //return 1 if loaded ok
#ifdef usedefaults
	return 1;
#endif
	byte b = eeprom_read_byte((unsigned char *) 0);
	byte c = eeprom_read_byte((unsigned char *) 1);
	if (b == guinosigold)
		c = 9; //before fancy parameter counter

	if (b == guinosig || b == guinosigold) {
		byte p = 0;

		for (int x = 4; p < c; x += 4) {
			unsigned long v = eeprom_read_byte((unsigned char *) x);
			v = (v << 8) + eeprom_read_byte((unsigned char *) x + 1);
			v = (v << 8) + eeprom_read_byte((unsigned char *) x + 2);
			v = (v << 8) + eeprom_read_byte((unsigned char *) x + 3);
			parms[p] = v;
			p++;
		}
		return 1;
	}
	return 0;
}

char * uformat(unsigned long val) {
	unsigned long d = 1000000000ul;
	for (byte p = 0; p < 10; p++) {
		mBuff[p] = '0' + (val / d);
		val = val - (val / d * d);
		d /= 10;
	}
	mBuff[10] = 0;
	return mBuff;
}

unsigned long rformat(char * val) {
	unsigned long d = 1000000000ul;
	unsigned long v = 0ul;
	for (byte p = 0; p < 10; p++) {
		v = v + (d * (val[p] - '0'));
		d /= 10;
	}
	return v;
}

void editParm(byte parmIdx) {
	unsigned long v = parms[parmIdx];
	byte p = 9; //right end of 10 digit number
	//display label on top line
	//set cursor visible
	//set pos = 0
	//display v

	LCD::gotoXY(8, 0);
	LCD::print("        ");
	LCD::gotoXY(0, 0);
	LCD::print(parmLabels[parmIdx]);
	LCD::gotoXY(0, 1);
	char * fmtv = uformat(v);
	LCD::print(fmtv);
	LCD::print(" OK XX");
	LCD::LcdCommandWrite(0b00001110);

	for (int x = 9; x >= 0; x--) { //do a nice thing and put the cursor at the first non zero number
		if (fmtv[x] != '0')
			p = x;
	}
	byte keyLock = 1;
	while (true) {

		if (p < 10)
			LCD::gotoXY(p, 1);
		if (p == 10)
			LCD::gotoXY(11, 1);
		if (p == 11)
			LCD::gotoXY(14, 1);

		if (keyLock == 0) {
			if (!(buttonState & lbuttonBit) && !(buttonState & rbuttonBit)) {// left & right
				LCD::LcdCommandWrite(0b00001100);
				return;
			} else if (!(buttonState & lbuttonBit)) {// left
				p = p - 1;
				if (p == 255)
					p = 11;
			} else if (!(buttonState & rbuttonBit)) {// right
				p = p + 1;
				if (p == 12)
					p = 0;
			} else if (!(buttonState & mbuttonBit)) {// middle
				if (p == 11) { //cancel selected
					LCD::LcdCommandWrite(0b00001100);
					return;
				}
				if (p == 10) { //ok selected
					LCD::LcdCommandWrite(0b00001100);
					parms[parmIdx] = rformat(fmtv);
					return;
				}

				byte n = fmtv[p] - '0';
				n++;
				if (n > 9)
					n = 0;
				if (p == 0 && n > 3)
					n = 0;
				fmtv[p] = '0' + n;
				LCD::gotoXY(0, 1);
				LCD::print(fmtv);
				LCD::gotoXY(p, 1);
				if (parmIdx == contrastIdx)//adjust contrast dynamically
					OCR0A = rformat(fmtv);

				//                 analogWrite(ContrastPin,rformat(fmtv));


			}

			if (buttonState != buttonsUp)
				keyLock = 1;
		} else {
			keyLock = 0;
		}
		buttonState = buttonsUp;
		delay2(125);
	}

}

void initGuino() { //edit all the parameters
	for (int x = 0; x < parmsLength; x++)
		editParm(x);
	save();
	injectorSettleTime = injhold;

	int0Func = processInjOpen;
	int1Func = processInjClosed;
	EIMSK &= ~(1 << INT0);
	EIMSK &= ~(1 << INT1);

	EICRA = (EICRA & ~((1 << ISC00) | (1 << ISC01)))
			| ((parms[injEdgeIdx] == 1 ? RISING : FALLING) << ISC00);
	EIMSK |= (1 << INT0);
	EICRA = (EICRA & ~((1 << ISC10) | (1 << ISC11)))
			| ((parms[injEdgeIdx] == 1 ? FALLING : RISING) << ISC10);
	EIMSK |= (1 << INT1);

	distancefactor = parms[vssPulsesPerMileIdx];
	fuelfactor = parms[microSecondsPerGallonIdx];
	if (parms[metricIdx] == 1){
		distancefactor /= 1.609;
		fuelfactor /= 3.785;
	}

	holdDisplay = 1;
}

unsigned long millis2() {
	return timer2_overflow_count * 64UL * 2 / (16000000UL / 128000UL);
}

void delay2(unsigned long ms) {
	unsigned long start = millis2();
	while (millis2() - start < ms)
		;
}

/* Delay for the given number of microseconds.  Assumes a 16 MHz clock.
 * Disables interrupts, which will disrupt the millis2() function if used
 * too frequently. */
void delayMicroseconds2(unsigned int us) {
	uint8_t oldSREG;
	if (--us == 0)
		return;
	us <<= 2;
	us -= 2;
	oldSREG = SREG;
	cli();
	// busy wait
	__asm__ __volatile__ (
			"1: sbiw %0,1" "\n\t" // 2 cycles
			"brne 1b" : "=w" (us) : "0" (us) // 2 cycles
	);
	// reenable interrupts.
	SREG = oldSREG;
}

void simpletx(char * string) {
	if (UCSR0B != (1 << TXEN0)) { //do we need to init the uart?
		UBRR0H = (unsigned char) (myubbr >> 8);
		UBRR0L = (unsigned char) myubbr;
		UCSR0B = (1 << TXEN0);//Enable transmitter
		UCSR0C = (3 << UCSZ00);//N81
	}
	while (*string) {
		while (!(UCSR0A & (1 << UDRE0)))
			;
		UDR0 = *string++; //send the data
	}
}

int main(void) {
	sei();
	sbi(TCCR0A, WGM01);
	sbi(TCCR0A, WGM00);
	sbi(TCCR0B, CS01);
	sbi(TCCR0B, CS00);
	sbi(TIMSK0, TOIE0);

	// set timer 1 prescale factor to 64
	sbi(TCCR1B, CS11);
	sbi(TCCR1B, CS10);
	// put timer 1 in 8-bit phase correct pwm mode
	sbi(TCCR1A, WGM10);
	// set timer 2 prescale factor to 64
	sbi(TCCR2B, CS22);
	// configure timer 2 for phase correct pwm (8-bit)
	sbi(TCCR2A, WGM20);

	// set a2d prescale factor to 128
	sbi(ADCSRA, ADPS2);
	sbi(ADCSRA, ADPS1);
	sbi(ADCSRA, ADPS0);

	// enable a2d conversions
	sbi(ADCSRA, ADEN);

	UCSR0B = 0;

	sei();

	timer2_overflow_count = 0;

	TCCR2A = 1 << WGM20 | 1 << WGM21;
	// set timer 2 prescale factor to 64
	TCCR2B = 1 << CS22;
	TIMSK2 |= 1 << TOIE2;
	TIMSK0 &= !(1 << TOIE0);

	setup();
	mainloop();

	return 0;
}


